package ysq.xyg.web.controller;

import cn.hutool.core.util.RandomUtil;
import com.github.pagehelper.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import ysq.xyg.base.controller.BaseController;
import ysq.xyg.base.model.dto.ReturnEntity;
import ysq.xyg.base.util.BaseConstant;
import ysq.xyg.base.util.BaseUtil;
import ysq.xyg.base.util.DateUtil;
import ysq.xyg.base.util.ProProperties;
import ysq.xyg.web.model.po.WebAccount;
import ysq.xyg.web.model.po.WebMember;
import ysq.xyg.web.model.po.WebUnionid;
import ysq.xyg.web.service.WebMemberService;
import ysq.xyg.web.service.WebUnionidService;
import ysq.xyg.wxapi.api.WxApiClient;
import ysq.xyg.wxapi.api.WxMemoryCacheClient;
import ysq.xyg.wxapi.model.base.*;
import ysq.xyg.wxapi.url.WxUrlUtil;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Date;

/**
 * 微信端controller层
 *
 * @author shengQiang yu
 * @date 2019-4-26 11:34:01
 */
@Controller
@RequestMapping("/weiXin")
public class WeXinController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(WeXinController.class);
    @Autowired
    private WebUnionidService webUnionidService;
    @Autowired
    private WebMemberService webMemberService;

    /**
     * oauth2.0跳转入库
     */
    @RequestMapping("/oauth")
    public ModelAndView oauth(HttpServletResponse response, HttpServletRequest request) throws IOException {
        String oauthType = request.getParameter("oauthType");
        String params = request.getParameter("params");
        String path = request.getParameter("path");
        String pathParams = request.getParameter("pathParams");
        String scope = OAuthScope.Base.toString();
        if (OAuthScope.Userinfo.toString().equals(oauthType)) {
            scope = OAuthScope.Userinfo.toString();
        }
        //获取缓存中的唯一账号
        WebAccount wxAccountInfo = WxMemoryCacheClient.getSingleWxAccountInfo();
        String url = WxUrlUtil.getOAuthCodeUrl(wxAccountInfo.getAppId(), wxAccountInfo.getBaseUrl() + ProProperties.getInstance().getWxOauthAcceptOpenidURL() + "?params=" + params + "&path=" + path + "&pathParams=" + pathParams, scope);
        return new ModelAndView("redirect:" + url);
    }

    /**
     * 获取openid 并跳转
     */
    @RequestMapping("/acceptOpenid")
    public ModelAndView acceptOpenid(HttpServletResponse response, HttpServletRequest request) throws IOException {
        String code = request.getParameter("code");
        // 获取到的state是模式页面组合，模式由一个字符，1表示base,2表示userinfo,其他为页面标识符
        String scope = request.getParameter("state");
        String params = request.getParameter("params");
        String path = request.getParameter("path");
        String pathParams = request.getParameter("pathParams");
        if ("null".equals(params)) {
            params = "";
        }
        if ("null".equals(path)) {
            path = "";
        }
        if ("null".equals(pathParams)) {
            pathParams = "";
        }
        ModelAndView modelAndView = null;
        try {
            // 获取缓存中的唯一账号
            WebAccount wxAccountInfo = WxMemoryCacheClient.getSingleWxAccountInfo();
            OAuthAccessToken oAuthAccessToken = WxApiClient.getOAuthOpenId(wxAccountInfo, code);
            log.info("openid is " + oAuthAccessToken.getOpenid());
            if (StringUtils.isEmpty(oAuthAccessToken.getOpenid())) {
                //跳转到oauth2.0的url
                String url = WxUrlUtil.getOAuthCodeUrl(wxAccountInfo.getAppId(), wxAccountInfo.getBaseUrl() + ProProperties.getInstance().getWxOauthAcceptOpenidURL() + "?params=" + params + "&path=" + path + "&pathParams=" + pathParams, OAuthScope.Base.toString());
                modelAndView = new ModelAndView("redirect:" + url);
            } else {
                //判断当前用户是否已经保存到数据库
                WebMember member = createUser(oAuthAccessToken, scope, params);
                if (member != null) {
                    String url = "";
                    if (StringUtil.isNotEmpty(path) && !("null".equalsIgnoreCase(path))) {
                        url = path + "?memberId=" + member.getId();
                        if (StringUtil.isNotEmpty(pathParams) && !("null".equalsIgnoreCase(pathParams))) {
                            String[] t = pathParams.split(",");
                            for (String c : t) {
                                String[] k = c.split("\\*");
                                url += "&" + k[0] + "=" + k[1];
                            }
                        }
                    }
                    modelAndView = acceptView(params, member, url);
                } else {
                    //跳转到oauth2.0的url
                    String url = WxUrlUtil.getOAuthCodeUrl(wxAccountInfo.getAppId(), wxAccountInfo.getBaseUrl() + ProProperties.getInstance().getWxOauthAcceptOpenidURL() + "?params=" + params + "&path=" + path + "&pathParams=" + pathParams, OAuthScope.Userinfo.toString());
                    modelAndView = new ModelAndView("redirect:" + url);
                }
                return modelAndView;
            }
        } catch (Exception e) {
            //跳转错误页面
            e.printStackTrace();
        }
        return modelAndView;
    }


    protected ModelAndView acceptView(String params, WebMember member, String url) {
        ModelAndView modelAndView = null;
        if (StringUtil.isNotEmpty(url)) {
            //参数页面
            modelAndView = new ModelAndView("redirect:" + url);
        } else {
            //默认页面
            modelAndView = new ModelAndView("redirect:" + ProProperties.getInstance().getWxOauthRredirectIndexURL(member.getId()));
        }
        return modelAndView;
    }

    /**
     * 创建本地用户
     *
     * @return
     */
    protected WebMember createUser(OAuthAccessToken oAuthAccessToken, String scope, String params) {
        WebMember member = webMemberService.selectEntityByOpenid(oAuthAccessToken.getOpenid());
        if (member == null) {
            // 用户不存在
            if (BaseConstant.OAUTH_USERINFO.equals(scope)) {
                //Oauth为获取用户信息模式
                User user = WxApiClient.getOAuthUserInfo(oAuthAccessToken);
                if (user != null) {
                    //将用户信息插入数据库
                    member = new WebMember();
                    int cardNo = webMemberService.selectMaxId();
                    if (cardNo < BaseConstant.MEMBER_MIN_DEFAULT_ID) {
                        cardNo = BaseConstant.MEMBER_MIN_DEFAULT_ID;
                    } else {
                        cardNo = cardNo + 1;
                    }
                    member.setId(BaseUtil.uId());
                    member.setCardNo(String.valueOf(cardNo));
                    member.setOpenid(user.getOpenid());
                    member.setHeadImage(user.getHeadimgurl());
                    member.setNickName(BaseUtil.encodeBase64String(user.getNickname()));
                    member.setRemoveTag(BaseConstant.BASE_REMOVE_TAG_NORMAL);
                    member.setCreateTime(new Date());
                    member.setGrade("1");
                    member.setUnionid(user.getUnionid());
                    member.setMoney(0.0);
                    if (StringUtil.isNotEmpty(params)) {
                        WebMember parent = webMemberService.selectEntityByUnionId(params);
                        if (parent != null) {
                            member.setOneCardNo(parent.getCardNo());
                            member.setTwoCardNo(parent.getOneCardNo());
                            member.setAreaCardNo(parent.getAreaCardNo());
                        }
                    }
                    int ids = webMemberService.insert(member);
                    if (ids > 0) {
                        return member;
                    }
                }
            }
            return null;
        } else {
            //用户存在
            return member;
        }
    }

    /**
     * 微信支付
     */
    @RequestMapping(value = "/pay", method = RequestMethod.POST)
    public @ResponseBody
    ReturnEntity pay(double money, String openid, String memberId, int num, HttpServletRequest request) {
        ReturnEntity returnEntity = new ReturnEntity();
        if (StringUtil.isNotEmpty(openid) && money > 0) {
            //获取订单号
            String id = "E" + System.currentTimeMillis() + RandomUtil.randomNumbers(6);
            //获取时间戳
            String timeStamp = DateUtil.create_timestamp();
            //获取ip地址
            String ip = request.getRemoteAddr();
            WebAccount wxAccountInfo = WxMemoryCacheClient.getSingleWxAccountInfo();
            String name = "购买" + num + "个袋子，消费" + money + "元";
            Return_pay pay = WxApiClient.WeiXin(num + "," + memberId, name, (int) (money * 100), openid, wxAccountInfo.getBaseUrl() + ProProperties.getInstance().getWxPayRequestURL(), id, ip, timeStamp, wxAccountInfo.getAppId(), wxAccountInfo.getPayMchId(), wxAccountInfo.getPayKey());
            pay.setId(id);
            log.info(pay.toString());
            if (pay == null || "".equals(pay)) {
                returnEntity = new ReturnEntity(BaseConstant.FAIL, BaseConstant.WECHAT_PAY_FAILL);
            } else {
                returnEntity.setData(pay);
            }
        } else {
            returnEntity = new ReturnEntity(BaseConstant.FAIL, BaseConstant.DATABASE_ERROR_CANSHU);
        }
        return returnEntity;
    }

    /**
     * 小程序微信支付
     */
    @RequestMapping(value = "/xcxPay", method = RequestMethod.POST)
    public @ResponseBody
    ReturnEntity xcxPay(double money, String openid, int num, String memberId, HttpServletRequest request) {
        ReturnEntity returnEntity = new ReturnEntity();
        String xcxOpenid = "";
        WebMember member = webMemberService.selectEntityByOpenid(openid);
        if (member != null && StringUtil.isNotEmpty(member.getUnionid())) {
            WebUnionid dbUnionidXcx = webUnionidService.selectByPrimaryKey(member.getUnionid());
            if (dbUnionidXcx != null && StringUtil.isNotEmpty(dbUnionidXcx.getXcxOpenid())) {
                xcxOpenid = dbUnionidXcx.getXcxOpenid();
            }
        }
        if (StringUtil.isNotEmpty(xcxOpenid) && money > 0) {
            //获取订单号
            String id = "E" + System.currentTimeMillis() + RandomUtil.randomNumbers(6);
            //获取时间戳
            String timeStamp = DateUtil.create_timestamp();
            //获取ip地址
            String ip = request.getRemoteAddr();
            WebAccount wxAccountInfo = WxMemoryCacheClient.getSingleWxAccountInfo();
            String name = "购买" + num + "个袋子，消费" + money + "元";
            if (StringUtil.isNotEmpty(name) && name.trim().length() > 50) {
                name = name.substring(0, 49);
            }
            Return_pay pay = WxApiClient.WeiXin(num + "," + memberId, name, (int) (money * 100), xcxOpenid, wxAccountInfo.getBaseUrl() + ProProperties.getInstance().getWxPayRequestURL(), id, ip, timeStamp, wxAccountInfo.getXcxAppId(), wxAccountInfo.getPayMchId(), wxAccountInfo.getPayKey());
            pay.setId(id);
            log.info(pay.toString());
            if (pay == null || "".equals(pay)) {
                returnEntity = new ReturnEntity(BaseConstant.FAIL, BaseConstant.WECHAT_PAY_FAILL);
            } else {
                returnEntity.setData(pay);
            }
        } else {
            returnEntity = new ReturnEntity(BaseConstant.FAIL, BaseConstant.DATABASE_ERROR_CANSHU);
        }
        return returnEntity;
    }

    /**
     * 微信支付发货通知
     */
    @RequestMapping(value = "/pay/sendNotice")
    public void sendNotice(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/xml;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        request.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();
        StringBuilder buff = new StringBuilder();

        buff.append("<?xml version= \"1.0\" encoding=\"utf-8\"?>");

        String postStr = null;
        try {
            postStr = this.readStreamParameter(request.getInputStream());
        } catch (Exception e) {
            log.error("Error SendProductNoticeServiceImpl method get postStr:" + e);
        }
        Return_Notice_Pay pay = new Return_Notice_Pay();
        if (null != postStr && !postStr.isEmpty()) {
            Document document = null;
            try {
                document = DocumentHelper.parseText(postStr);
            } catch (Exception e) {
                log.error("Error SendProductNoticeServiceImpl method get document:" + e);
            }
            if (null == document) {
                buff.append("<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[支付失败]]></return_msg></xml>");
                out.print(buff.toString());
                return;
            }
            Element root = document.getRootElement();
            String return_code = root.elementText("return_code");
            log.info("return_code:" + return_code);
            pay.setReturn_code(return_code);
            if ("SUCCESS".equalsIgnoreCase(return_code)) {
                String appid = root.elementText("appid");
                pay.setAppid(appid);
                log.info("appid:" + appid);

                String mch_id = root.elementText("mch_id");
                pay.setMch_id(mch_id);
                log.info("mch_id:" + mch_id);

                String nonce_str = root.elementText("nonce_str");
                pay.setNonce_str(nonce_str);
                log.info("nonce_str:" + nonce_str);

                String device_info = root.elementText("device_info");
                pay.setDevice_info(device_info);
                log.info("device_info:" + device_info);

                String sign = root.elementText("sign");
                pay.setSign(sign);
                log.info("sign:" + sign);

                String result_code = root.elementText("result_code");
                pay.setResult_code(result_code);
                log.info("result_code:" + result_code);

                if ("SUCCESS".equalsIgnoreCase(result_code)) {
                    String openid = root.elementText("openid");
                    pay.setOpenid(openid);
                    log.info("openid:" + openid);

                    String is_subscribe = root.elementText("is_subscribe");
                    pay.setIs_subscribe(is_subscribe);
                    log.info("is_subscribe:" + is_subscribe);

                    String trade_type = root.elementText("trade_type");
                    pay.setTrade_type(trade_type);
                    log.info("trade_type:" + trade_type);

                    String bank_type = root.elementText("bank_type");
                    pay.setBank_type(bank_type);
                    log.info("bank_type:" + bank_type);

                    String total_fee = root.elementText("total_fee");
                    pay.setTotal_fee(Integer.parseInt(total_fee));
                    log.info("total_fee:" + total_fee);

                    String coupon_fee = root.elementText("coupon_fee");
                    if (coupon_fee != null && !"".equals(coupon_fee)) {
                        pay.setCoupon_fee(Integer.parseInt(coupon_fee));
                    }
                    log.info("coupon_fee:" + coupon_fee);

                    String fee_type = root.elementText("fee_type");
                    pay.setFee_type(fee_type);
                    log.info("fee_type:" + fee_type);

                    String transaction_id = root.elementText("transaction_id");
                    pay.setTransaction_id(transaction_id);
                    log.info("transaction_id:" + transaction_id);

                    String out_trade_no = root.elementText("out_trade_no");
                    pay.setOut_trade_no(out_trade_no);
                    log.info("out_trade_no:" + out_trade_no);

                    String attach = root.elementText("attach");
                    pay.setAttach(attach);
                    log.info("attach:" + attach);

                    String time_end = root.elementText("time_end");
                    pay.setTime_end(time_end);
                    log.info("attach:" + attach);

                } else {
                    String err_code = root.elementText("err_code");
                    pay.setErr_code(err_code);
                    log.info("err_code:" + err_code);

                    String err_code_des = root.elementText("err_code_des");
                    pay.setErr_code_des(err_code_des);
                    log.info("err_code_des:" + err_code_des);
                }
            } else {
                String return_msg = root.elementText("return_msg");
                pay.setReturn_msg(return_msg);
                log.info("return_msg:" + return_msg);
            }
        }
        log.info("pay.toString():" + pay.toString());
        if ("SUCCESS".equalsIgnoreCase(pay.getReturn_code()) && "SUCCESS".equalsIgnoreCase(pay.getResult_code())) {
            //获取参数
            //设置值
            String[] arg = pay.getAttach().split(",");
            String orderId = arg[0];
            // 更新成功
            buff.append("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[支付成功]]></return_msg></xml>");
        } else {
            buff.append("<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[支付失败]]></return_msg></xml>");
        }
        log.info("out :" + buff.toString());
        out.print(buff.toString());
    }

    //从输入流读取post参数
    protected String readStreamParameter(ServletInputStream in) {
        StringBuilder buffer = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != reader) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return buffer.toString();
    }
}
